﻿#region The MIT License (MIT)
// The MIT License (MIT)
// Copyright (c) 2010 - 2013 Sovacode LLC
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated 
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all copies or substantial portions
// of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#endregion

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Sovacode.RubiksCube.Common.Base;
using Sovacode.RubiksCube.Common.Exceptions;

namespace Sovacode.RubiksCube.Common.Service.Progress
{
    /// <summary>
    /// Class ProgressService
    /// </summary>
    internal class ProgressService : NotifyValidatedObject, IProgressService
    {
        #region Private Fields

        /// <summary>
        /// The current work
        /// </summary>
        private string _currentWork = string.Empty;

        /// <summary>
        /// The _current task
        /// </summary>
        private Task _currentTask = null;

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the task.
        /// </summary>
        /// <value>The task.</value>
        private Task Task
        {
            get { return _currentTask; }
            set
            {
                _currentTask = value;

                OnPropertyChanged(() => CurrentWorkName);
                OnPropertyChanged(() => Working);
            }
        }

        /// <summary>
        /// Gets the current work.
        /// </summary>
        /// <value>The current work.</value>
        public string CurrentWorkName
        {
            get { return Working ? _currentWork : string.Empty; }
            private set
            {
                if (string.CompareOrdinal(value, _currentWork) == 0)
                {
                    return;
                }

                _currentWork = value;

                OnPropertyChanged(() => CurrentWorkName);
                OnPropertyChanged(() => Working);
            }
        }

        /// <summary>
        /// Gets a value indicating whether this <see cref="IProgressService" /> is working.
        /// </summary>
        /// <value><c>true</c> if working; otherwise, <c>false</c>.</value>
        public bool Working
        {
            get { return Task != null && _currentTask.IsCompleted; }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Starts the work.
        /// </summary>
        /// <param name="work">The work.</param>
        /// <param name="onCompleted">The configuration completed.</param>
        /// <param name="workName">Name of the work.</param>
        public void StartWork(Action work, Action onCompleted = null, string workName = "")
        {
            if (Working)
            {
                throw new CrossThreadException();
            }

            if (work == null)
            {
                throw new ArgumentNullException("work");
            }

            var task = Task.Factory.StartNew(work);

            Task = task;

            CurrentWorkName = string.IsNullOrWhiteSpace(workName) ? string.Format("Work '{0}'", Guid.NewGuid()) : workName;

            task.GetAwaiter().OnCompleted(() => OnCompleted(onCompleted));
        }

        /// <summary>
        /// Starts the work.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="work">The work.</param>
        /// <param name="onCompleted">The configuration completed.</param>
        /// <param name="workName">Name of the work.</param>
        public void StartWork<T>(Func<T> work, Action<T> onCompleted = null, string workName = "")
        {
            if (Working)
            {
                throw new CrossThreadException();
            }

            if (work == null)
            {
                throw new ArgumentNullException("work");
            }

            var task = Task<T>.Factory.StartNew(work);

            Task = task;

            CurrentWorkName = string.IsNullOrWhiteSpace(workName) ? string.Format("Work '{0}'", Guid.NewGuid()) : workName;

            task.GetAwaiter().OnCompleted(() => OnCompleted(task.Result, onCompleted));
        }

        #endregion

        #region Private Methods
        
        /// <summary>
        /// Called when task completed.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="result">The result.</param>
        /// <param name="onCompleted">The configuration completed.</param>
        private void OnCompleted<T>(T result, Action<T> onCompleted = null)
        {
            if (onCompleted != null)
            {
                onCompleted.Invoke(result);
            }

            Trace.WriteLine(string.Format("Work '{0}' completed...", _currentWork));

            OnPropertyChanged(() => Working);
            OnPropertyChanged(() => CurrentWorkName);
        }

        /// <summary>
        /// Called when task completed.
        /// </summary>
        /// <param name="onCompleted">The configuration completed.</param>
        private void OnCompleted(Action onCompleted = null)
        {
            if (onCompleted != null)
            {
                onCompleted.Invoke();
            }

            Trace.WriteLine(string.Format("Work '{0}' completed...", _currentWork));

            OnPropertyChanged(() => Working);
            OnPropertyChanged(() => CurrentWorkName);
        }

        #endregion

    }
}